﻿/** @class FieldGraphGenerator
 *  @brief Genetate a field graph, whcih contains a scalar field with a
 *  scalar bar, and a vector field. This class support change different field
 *  from the given polyDatas.
 */

#ifndef QVTK_FIELDGRAPHGENERATOR_H
#define QVTK_FIELDGRAPHGENERATOR_H

#include <vector>
#include <array>
#include <QObject>

class vtkScalarBarActor;
class vtkActor;
class vtkLODActor;
class vtkGlyph3D;
class vtkLookupTable;
class vtkPolyDataMapper;
class vtkDataSetMapper;
class vtkRenderer;
class vtkArrowSource;
class vtkRenderWindowInteractor;
class vtkScalarBarWidget;
class vtkMaskPoints;
class vtkDoubleArray;
class vtkDataSet;
class vtkDataArray;

namespace pst
{

    class FieldGraphGenerator : public QObject
    {
        Q_OBJECT
    public:
        explicit FieldGraphGenerator(QObject* parent = nullptr);
        ~FieldGraphGenerator();

    public:
        enum class FieldType
        {
            SCALARS,
            VECTORS,
        };

        enum class StructureType
        {
            POINTS,
            CELLS
        };

        struct ArrayInformation
        {
            int arrayIndex;
            QString arrayName;
            int arrayComponent;
            StructureType type;
        };

    public:
        //设置polyData，用于显示
        //包含几何和拓扑结构，同时包括场数据
        void SetInputDataSet(vtkDataSet* data);

        void SetScalarsScalarBarWidgetInteractor(vtkRenderWindowInteractor* iren);
        void SetVectorsScalarBarWidgetInteractor(vtkRenderWindowInteractor* iren);

        void PrintInputDataSetInformation();
        //获取polyData，可用于重新为其设置属性
        vtkDataSet* GetInputData();

        //应该在没有设置标量场时获取，并且只获取一次。因为设置标量场可能会增加属性。
        //获取属性表points单元下标及其对应的数据名称
        std::vector<ArrayInformation> GetPointsArrayIndexAndNames();
        //获取属性表cells单元下标及其对应的数据名称
        std::vector<ArrayInformation> GetCellsArrayIndexAndNames();

        /**
         * scalar bar 控制方法,包括标量场和矢量场
         */
         //设置scalarBar的映射范围,默认范围为标量场的范围
        void SetScalarBarRange(const double minValue, const double maxValue, FieldType type);
        //设置scalarBar的映射范围为激活的标量场的范围，先设置标量场属性否则不生效，直接返回false
        bool SetScalarBarRangeToAuto(FieldType type);
        double* GetScalarBarRange(FieldType type);

        //标题名称
        QString GetScalarBarTitle(FieldType type);
        void SetScalarBarTitle(const QString& newTitle, FieldType type);

        //设置scalarBar标签的个数（细分程度）,超过64个按64处理
        int GetScalarBarNumberOfLabels(FieldType type);
        void SetScalarBarNumberOfLabels(const int num, FieldType type);

        //标量场ScalarBarActor
        vtkScalarBarActor* GetScalarsFieldScalarBarActor();
        //矢量场ScalarBarActor
        vtkScalarBarActor* GetVectorsFieldScalarBarActor();

        /**
         * 标量场控制方法
         */

         //使用数组下标来设置作为标量属性的数组下标
         //不进行下标范围检查，由用户保证
        bool SetActiveScalarAttribute(int index,
            const StructureType type, const int compIndex = -1);

        //设置用于映射标量场的颜色映射表，默认从蓝色到红色
        void SetScalarsLookupTable(vtkLookupTable* table);
        vtkLookupTable* GetScalarsLookupTable();

        void UpdateScalarFieldActor();  //更新标量场actor

        //    void TurnOnScalarBarWidget(vtkRenderWindowInteractor* iren);

            //获取标量场actor(含颜色)，必须设置标量场属性才能获取
        vtkActor* GetScalarsFieldActor();

        /**
         * 矢量场控制方法
         */

         //使用数组下标来设置作为矢量属性的数组下标
         //不进行下标范围检查，由用户保证
        bool SetActiveVectorsAttribute(int index, const StructureType type);

        //设置矢量场稀疏采样的间隔
        void SetMaskPointsStride(const int stride);

        //表示矢量的箭头的基本尺寸，默认0.6
        void SetArrowSize(const double size);
        double GetArrowSize();

        //获取用于显示矢量箭头的数据源
        vtkGlyph3D* GetArrowGlyph();

        //设置用于显示矢量场箭头颜色的颜色映射表 可不设置
        void SetVectorsLookupTable(vtkLookupTable* table);

        //更新矢量场actor
        void UpdateVectorsFieldActor();

        //获取矢量箭头，必须先设置矢量场属性才能获取
        //其颜色映射表默认为矢量场模值的大小
        vtkActor* GetVectorsFieldActor();

        /**
         * 纯几何actor
         */
         //设置颜色
        void SetGeometryActorColor(const double r, const double g, const double b);
        //更新几何actor
        void UpdateGeometryActor();
        vtkActor* GetGeometryActor();

    private:
        void InitDefaultLookupTable();
        void ReinitVectorFiledSetting();
        //结合actor,配合矢量场使用
        void InitGeometryActor();
        void InitScalarsFiledPipeLine();
        void InitVectorsFiledPipeLine();
        void PrintArrayInformation(vtkDataArray* array);

        //提取每个属性的单个分量，最初设计目的是 用于将含有多个成分的属性（如三个组分的数据）设置为标量场属性时，
        //提取其中一个单独的组分作为显示。component为-1时，返回magnitude。
        bool GetSingleComponent(vtkDataArray* array, const int component, vtkDoubleArray* returnArray);

    private:

        bool m_isScalarAttributeSet;
        bool m_isVectorAttributeSet;
        bool m_isVectorMaskPointsStrideSet; //是否手动设置了采样间隔

        vtkDataSet* m_inputData;

        vtkArrowSource* m_arrow;

        vtkActor* m_geometryActor;
        vtkActor* m_scalarFieldActor;
        //    vtkActor* m_vectorFieldActor;
        vtkLODActor* m_vectorFieldActor_lod;
        vtkScalarBarWidget* m_scalarsScalarBarWidget;
        vtkScalarBarWidget* m_vectorsScalarBarWidget;
        vtkScalarBarActor* m_scalarsScalarBarActor;
        vtkScalarBarActor* m_vectorsScalarBarActor;

        vtkDataSetMapper* m_geometryMapper;      //几何mapper
        vtkDataSetMapper* m_scalarsFieldMapper;   //标量场mapper
        vtkPolyDataMapper* m_vectorsFieldMapper;  //矢量场mapper
        vtkPolyDataMapper* m_vectorsFieldMapper_lowRes;  //低分辨率矢量场mapper

        vtkLookupTable* m_scalarsFieldLookupTable;
        vtkLookupTable* m_vectorsFieldLookupTable;
        vtkGlyph3D* m_arrowGlyph3D;
        vtkGlyph3D* m_arrowGlyph3D_lowRes;
        int m_maskPointsStride;
        vtkMaskPoints* m_maskPts;
        vtkMaskPoints* m_maskPts_lowRes;

        double m_arrowSize;

        StructureType m_currentUsingScalarType;   //表明当前使用的标量属性属于POINTS的还是CELLS
        StructureType m_currentUsingVectorType;   //表明当前使用的矢量属性属于POINTS的还是CELLS

        int m_currentAddedPointArrayIndex; //设置标量场属性，当多组分array的一个分类被添加后，下次设置时，用于移除。
        int m_currentAddedCellArrayIndex; //设置标量场属性，当多组分array的一个分类被添加后，下次设置时，用于移除。

        std::array<double, 2> m_currentScalarsArrayRange; //当前设置的标量场数值范围
        std::array<double, 2> m_currentVectorsArrayRange; //当前设置的矢量场数值范围
    };


} //namespace pst
#endif // PST_FIELDGRAPHGENERATOR_H
